package com.zrwl.poscloud.commons.config.log;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.LinkedList;

/**
 * @author chh
 * @version 1.0.0
 * @class MyApiLogAspect
 * @classdesc 实现ApiLog的功能，即打印请求日志和响应日志
 * @date 2020/8/26  15:07
 * @see
 * @since
 */
@Aspect
@Component
@Slf4j
public class MyApiLogAspect {
    /**
     * 记录业务请求的时间
     */
    private long req;

    private String reqTime;
    private String requestUrl;

    /**
     * @param: void
     * @return: void
     * @desc: 定义空方法用于切点表达式
     * @see
     * @since
     */
    @Pointcut("@annotation(ApiLog)")
    public void pointcut() {
        //do nothing just for filtering
    }

    @Pointcut("execution(* com.zrwl.poscloud..*Api.*(..))")
    public void pointcutApi() {
        //do nothing just for filtering
    }

    /**
     * @param: [joinPoint]
     * @return: void
     * @desc: 在进入controller之前拦截并打印请求报文日志
     * @see
     * @since
     */
    @Before("pointcut()||pointcutApi()")
    public void printRequestDatagram(JoinPoint joinPoint) {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        String ip = getIpAddress(request);
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");
        req = System.currentTimeMillis();
        reqTime = dateFormat.format(new Date(req));

        // 获取请求参数进行打印
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        // 参数名数组
        String[] parameterNames = ((MethodSignature) signature).getParameterNames();
        // 构造参数组集合
        JSONArray argList = new JSONArray();
        for (Object arg : joinPoint.getArgs()) {
            // request/response无法使用toJSON
            if (arg instanceof HttpServletRequest) {
                argList.add("request");
            } else if (arg instanceof HttpServletResponse) {
                argList.add("response");
            } else if (arg instanceof BindingResult) {
                argList.add("bindingResult");
            } else  if (arg instanceof LinkedList) {
                argList.add("linkedList");
            } else {
                argList.add(JSON.toJSON(arg));
            }
        }

        String url = request.getRequestURL().toString();
        int x = url.substring(10).indexOf("/");
        requestUrl = url.substring(x + 10);
        log.debug("\n==> 拦截到请求\n"
                + "==> 请求时间：" + reqTime + "\n"
                + "==> 请求接口：" + request.getMethod() + " " + url + "\n"
                + "==> 请求方法：" + requestUrl + "\n"
                + "==> 参数内容：" + argList.toJSONString());
        log.info("\n==> 请求时间：" + reqTime + "\n"
                + "==> 请求接口：" + request.getMethod() + " " + url + "\n");
    }

    /**
     * @param: [joinPoint]
     * @return: java.lang.Object
     * @desc: 返回信息后，打印应答报文的日志
     * @see
     * @since
     */
    @Around("pointcut()||pointcutApi()")
    public Object printResponseDatagram(ProceedingJoinPoint joinPoint) throws Throwable {
        Object result = null;
        result = joinPoint.proceed();
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");
        long resp = System.currentTimeMillis();
        String respTimeStr = dateFormat.format(new Date(resp));
        long respTime = resp - req;
        log.debug("\n<== 响应请求:" + requestUrl + "\n"
                + "<== 响应时间：" + respTimeStr + "\n"
                + "<== 响应耗时：" + respTime + "ms\n"
                + "<== 应答内容：" + result);
        log.info("\n<== 响应请求:" + requestUrl + "\n"
                + "<== 响应时间：" + respTimeStr + "\n"
                + "<== 响应耗时：" + respTime + "ms（应答内容在debug日志模式下查看）\n");
        return result;
    }

    /**
     * @param: [request]
     * @return: java.lang.String
     * @desc: 获取IP地址
     * @see
     * @since
     */
    private String getIpAddress(HttpServletRequest request) {
        final String UNKNOWN = "unknown";
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }
}
